﻿/*
 * Copyright © 2020, Wuxi Safover Intelligent Technology Co., Ltd. All rights reserved.
 * 
 * 无锡安永智能科技有限公司版权所有，未经许可，不得用于商业用途
 * 
 * Visit: https://www.safover.com/ 
 */

using System;
using System.Diagnostics;
using System.Drawing;
using Safover.LidarCollector.Core;

namespace Video.Hik
{
    public class HikCamera : ICamera, IDisposable
    {
        public bool IsReady { get; private set; }

        public int LogLevel { get; set; } = 3;

        private int UserId { get; set; } = -1;

        private int RealPlayHandle { get; set; } = -1;

        public bool HasLoggedIn => UserId != CHCNetSDK.UNLOGGED;

        public bool IsRealPlaying => RealPlayHandle != -1;

        public CameraStatusChangedEventHandler CameraStatusChanged { get; set; }


        //IntPtr PlayWnd { get; set; }
        //REALDATACALLBACK RealPlayDataCallBackMethod;

        public HikCamera() { }

        #region IDisposable

        public void Dispose() => Dispose(true);

        ~HikCamera()
        {
            Dispose(false);
        }

        private void Dispose(bool disposing)
        {
            if (disposing)
            {
                // Dispose any disposable fields here
                GC.SuppressFinalize(this);
            }

            CloseCamera();
        }

        #endregion

        public bool OpenCamera()
        {
            MsgReturnedDto result = TryOpenCamera();
            Debug.WriteLine(result);

            IsReady = result.Success;

            return result.Success;
        }

        public bool CloseCamera()
        {
            MsgReturnedDto result = TryCloseCamera();
            Debug.WriteLine(result == null ? "" : result.ToString());

            IsReady = false;

            return result.Success;
        }

        public bool StartVideo(IntPtr playWnd)
        {
            MsgReturnedDto result = TryStartVideo(playWnd);
            Debug.WriteLine(result);

            return result.Success;
        }

        public bool StopVideo()
        {
            MsgReturnedDto result = TryStopVideo();
            Debug.WriteLine(result);

            return result.Success;
        }

        public bool CaptureImage(ImageType imageType, string fileFullName)
        {
            var channel = 1;
            NET_DVR_JPEGPARA lpJpegPara = new NET_DVR_JPEGPARA();
            lpJpegPara.wPicQuality = 0; //图像质量 Image quality
            lpJpegPara.wPicSize = 0xff;

            if (!CHCNetSDK.NET_DVR_CaptureJPEGPicture(UserId, channel, ref lpJpegPara, fileFullName))
            {
                var code = CHCNetSDK.NET_DVR_GetLastError();
                var reason = new MsgReturnedDto(false, $"Initialization error：Code={code}.");
                Debug.WriteLine(reason);

                return false;
            }

            return true;
        }

        public Image CaptureVideoImage(ImageType imageType, string fileFullName)
        {
            if (!CHCNetSDK.NET_DVR_CapturePicture(RealPlayHandle, fileFullName))
            {
                var code = CHCNetSDK.NET_DVR_GetLastError();
                var reason = new MsgReturnedDto(false, $"Capture real play image failed：Code={code}.");
                Debug.WriteLine(reason);

                return null;
            }

            return Image.FromFile(fileFullName);
        }

        private MsgReturnedDto TryOpenCamera()
        {
            // Initialize
            var success = CHCNetSDK.NET_DVR_Init();

            if (!success)
            {
                var code = CHCNetSDK.NET_DVR_GetLastError();
                return new MsgReturnedDto(false, $"Initialization error：Code={code}.");
            }

            // Set log
            if (!CHCNetSDK.NET_DVR_SetLogToFile(LogLevel, "SdkLog", true))
            {
                var code = CHCNetSDK.NET_DVR_GetLastError();
                return new MsgReturnedDto(false, $"Set log error：Code={code}.");
            }

            // Login
            return Login();
        }

        private MsgReturnedDto TryCloseCamera()
        {
            if (IsRealPlaying)
            {
                if (!StopVideo())
                {
                    var code = $"Stop video error：Code={CHCNetSDK.NET_DVR_GetLastError()}.";
                    var reason = new MsgReturnedDto(false, code);
                    Debug.WriteLine(reason);
                }
            }

            if (HasLoggedIn)
            {
                var logoutResult = Logout();
                Debug.WriteLine(logoutResult);
            }

            if (!CHCNetSDK.NET_DVR_Cleanup())
            {
                var code = $"Cleanup camera error：Code={CHCNetSDK.NET_DVR_GetLastError()}.";
                return new MsgReturnedDto(false, code);
            }

            return new MsgReturnedDto(true, "Camera successfully closed.");
        }

        private MsgReturnedDto Login()
        {
            if (HasLoggedIn) new MsgReturnedDto(true, "Already logged in.");

            NET_DVR_DEVICEINFO_V30 DeviceInfo = new NET_DVR_DEVICEINFO_V30();

            // TODO:
            UserId = CHCNetSDK.NET_DVR_Login_V30("192.168.10.64", 8000, "admin", "admin123", ref DeviceInfo);

            MsgReturnedDto result;
            uint code = CHCNetSDK.NET_DVR_GetLastError();
            if (UserId == CHCNetSDK.INVOKE_FAILURE)
            {
                result = new MsgReturnedDto(false, $"Login error：Code={code}.");
            }
            else
            {
                result = new MsgReturnedDto(true, $"Successfully logged in. Code={code}.");
            }

            return result;
        }

        private MsgReturnedDto Logout()
        {
            if (!HasLoggedIn) new MsgReturnedDto(true, "Already logged out.");

            if (IsRealPlaying)
            {
                if (!StopVideo())
                {
                    var message = $"Stop video error：Code={CHCNetSDK.NET_DVR_GetLastError()}.";
                    var reason = new MsgReturnedDto(false, message);
                    Debug.WriteLine(reason);
                }
            }

            if (!CHCNetSDK.NET_DVR_Logout_V30(UserId))
            {
                var message = $"Logout error：Code={CHCNetSDK.NET_DVR_GetLastError()}.";
                return new MsgReturnedDto(false, message);
            }

            return new MsgReturnedDto(true, "Successfully logged out.");
        }

        private MsgReturnedDto TryStartVideo(IntPtr playWnd)
        {
            if (RealPlayHandle != CHCNetSDK.INVOKE_FAILURE)
            {
                if (!CHCNetSDK.NET_DVR_StopRealPlay(RealPlayHandle))
                {
                    Debug.WriteLine(new MsgReturnedDto(false, $"Stop real play failed：Code={CHCNetSDK.NET_DVR_GetLastError()}."));
                }
            }

            NET_DVR_PREVIEWINFO lpPreviewInfo = new NET_DVR_PREVIEWINFO();
            lpPreviewInfo.hPlayWnd = playWnd;//预览窗口
            lpPreviewInfo.lChannel = 1;//预览的设备通道
            lpPreviewInfo.dwStreamType = 0;//码流类型：0-主码流，1-子码流，2-码流3，3-码流4，以此类推
            lpPreviewInfo.dwLinkMode = 0;//连接方式：0- TCP方式，1- UDP方式，2- 多播方式，3- RTP方式，4-RTP/RTSP，5-RSTP/HTTP 
            lpPreviewInfo.bBlocked = true; //0- 非阻塞取流，1- 阻塞取流
            lpPreviewInfo.dwDisplayBufNum = 15; //播放库播放缓冲区最大缓冲帧数
            lpPreviewInfo.byProtoType = 0;
            lpPreviewInfo.byPreviewMode = 0;

            REALDATACALLBACK RealData = new REALDATACALLBACK(RealDataCallBack);//预览实时流回调函数
            IntPtr pUser = new IntPtr();//用户数据

            RealPlayHandle = CHCNetSDK.NET_DVR_RealPlay_V40(UserId, ref lpPreviewInfo, null/*RealData*/, pUser);

            uint tmpCode = CHCNetSDK.NET_DVR_GetLastError();
            if (RealPlayHandle == CHCNetSDK.INVOKE_FAILURE)
            {
                return new MsgReturnedDto(false, $"Start real play failed：Code={tmpCode}.");
            }

            return new MsgReturnedDto(true, $"Start real play successed. Code={tmpCode}.");
        }

        private MsgReturnedDto TryStopVideo()
        {
            if (!IsRealPlaying) new MsgReturnedDto(true, "Already stopped.");

            uint tmpCode = CHCNetSDK.NET_DVR_GetLastError();
            if (!CHCNetSDK.NET_DVR_StopRealPlay(RealPlayHandle))
            {
                return new MsgReturnedDto(false, $"Stop real play failed：Code={tmpCode}.");
            }

            RealPlayHandle = -1;
            return new MsgReturnedDto(true, $"Stop real play successed. Code={tmpCode}.");
        }

        private void RealDataCallBack(int lRealHandle, uint dwDataType, ref byte pBuffer, uint dwBufSize, IntPtr pUser) { }
    }
}
